/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief Simple class to handle last access point(s) storing.
 * \author Antti Maula <antti.maula@tkk.fi>
 *
 *
 * Simple example application:
 *
 ******************************************************************************
 */
#ifndef _GIMSRC_GIMBO_LASTAPS_HPP_
#define _GIMSRC_GIMBO_LASTAPS_HPP_

//#include <vector>
#include <string>
#include <stdlib.h>

#include <list>
//#include <sstream>
//#include <iostream>
#include <fstream>
#include <algorithm>


typedef std::list<std::string> THostnameArray;
typedef std::list<unsigned int> TPortArray;


/** Handler class for reading/writing last accessed GIMnet AP list.
 *
 * The class has the easiest interface possible. No parameters are
 * required for constructing.
 *
 * Usage:
 *
 * Each time a connection is made to a particular AP, store it
 * to the class by calling function AddLastAccessPoint(). If the entry
 * is already in the list, it will be moved to top of the list.
 *
 * When constructing selection boxes, call function
 * GetLastAccessPoints().  The function will return an array of
 * SAccessPoint elements. (As defined by type TAccessPointArray). It
 * may contain zero or more elements.
 * 
 * The class has support for custom backlog size (See constructor) and
 * the default is set to 5 last entries.
 *
 * When the class instance is destroyed, the current state of the
 * class is automatically stored to storage file. Storage file name
 * can be given in constructor if required. State can also be manually
 * written to file by calling the WriteFile() function.
 * 
 *
 */
class CLastAPs
{
public:
  /** Constructor.
   *
   * @param[in] aLastAPsFilename Filename to use as storage. 
   *                             Default is: ".gimbo_lastAPs"
   * @param[in] aBacklogSize     Size of backlog (now many entries are
   *                             maintained in the list) Default is 5.
   */
  CLastAPs(const std::string &aHostnamesFile = ".gimbo_lastAPs.hostnames", 
           const std::string &aPortsFile = ".gimbo_lastAPs.ports", 
           const unsigned int aBacklogSize = 5) 
    : iTargetFileHostnames(aHostnamesFile),
      iTargetFilePorts(aPortsFile),
      iHostnameArray(),
      iPortArray(),
      iBacklogSize(aBacklogSize)
  {
    ReadFiles();
  }
  
  ~CLastAPs() 
  {
    WriteFiles();
  }
  

  /** Read last accessed APs list from file.
   *
   * This function attempts to read in the last accessed
   * list. The filename is set in the class constructor.
   *
   * @return 'true' if file was read succesfully,
   *         'false' on any error. Note: If the file doesn't exist,
   *         false is returned. So reading entries function failure 
   *         can usually be ignored.
   */
  bool ReadFiles(void)
  {
    iHostnameArray.clear();
    iPortArray.clear();

    bool result = true;
    
    std::ifstream file_h (iTargetFileHostnames.c_str(), 
                          std::ios::in );
    if (file_h.is_open()) {
      char line[2048];
      do {
        file_h.getline(line,sizeof(line));
        if (!file_h.eof()) {
          AddHostname(line);
        }
      } while(!file_h.eof());
    } else result = false;


    std::ifstream file_p (iTargetFilePorts.c_str(), 
                          std::ios::in );
    if (file_p.is_open()) {
      char line[2048];
      do {
        file_p.getline(line,sizeof(line));
        if (!file_p.eof()) {
          AddPort(atoi(line));
        }
      } while(!file_p.eof());
    } else result = false;
    
    return result;
  }

  
  /** Write last accessed APs list to file.
   * 
   * This function writes the current list of last accessed access
   * points to file. The filename is set in the class constructor.
   * 
   * @return 'true' if file was written succesfully.
   *         'false' on any error.
   */
  bool WriteFiles(void)
  { 
    bool result = false;

    std::ofstream file_h (iTargetFileHostnames.c_str(), 
                          std::ios::out );
    if (file_h.is_open()) {
      for(THostnameArray::const_iterator i = iHostnameArray.begin();
          i != iHostnameArray.end();
          ++i) {
        file_h << *i << std::endl;
      }
      file_h.close();
      result = true;
    } 

    std::ofstream file_p (iTargetFilePorts.c_str(), 
                          std::ios::out );
    if (file_p.is_open()) {
      for(TPortArray::const_iterator i = iPortArray.begin();
          i != iPortArray.end();
          ++i) {
        file_p << *i << std::endl;
      }
      file_p.close();
      result &= true;
    } 

    return result;
  }

  
  /** Get an array of last accessed ports.
   *
   * This function returns an array of most recently accessed ports.
   * Last accessed is always the last element in the list.
   *
   * @return array of ports.
   *
   */
  const TPortArray &GetLastPorts(void) const {
    return iPortArray;
  }


  /** Get an array of last accessed hostnames.
   *
   * This function returns an array of last accessed hostnames.
   * THe most recent entry is always the last one.
   *
   * @return array of hostnames.
   */
  const THostnameArray &GetLastHostnames(void) const {
    return iHostnameArray;
  }


  /** Register new hostname.
   *
   * This function registers a new hostname. If the hostname already
   * exists within the backlog, it will be moved to 'most-recent' slot
   * of the list. The function automatically limits the number of recent hosts
   * to 
   *
   * @param[in] aHostname Host name to register.
   */
  void AddHostname(const std::string &aHostname) {
    THostnameArray::iterator i = find(iHostnameArray.begin(),
                                      iHostnameArray.end(),
                                      aHostname);
    if (i == iHostnameArray.end()) {
      iHostnameArray.push_back(aHostname);

    } else {
      iHostnameArray.erase(i);
      iHostnameArray.push_back(aHostname);

    }

    while(iHostnameArray.size() > iBacklogSize) iHostnameArray.pop_front();
  }

  /** Register new port.
   *
   * This function registers a new hostname. If the hostname already
   * exists within the backlog, it will be moved to 'most-recent' slot
   * of the list. The function automatically limits the number of recent hosts
   * to 
   *
   * @param[in] aPort Host name to register.
   */
  void AddPort(const unsigned int &aPort) {
    TPortArray::iterator i = find(iPortArray.begin(),
                                  iPortArray.end(),
                                  aPort);
    if (i == iPortArray.end()) {
      iPortArray.push_back(aPort);
      
    } else {
      iPortArray.erase(i);
      iPortArray.push_back(aPort);

    }

    while(iPortArray.size() > iBacklogSize) iPortArray.pop_front();
  }


private:
  std::string iTargetFileHostnames;
  std::string iTargetFilePorts;
  THostnameArray iHostnameArray;
  TPortArray iPortArray;
  unsigned int iBacklogSize;           ///< Size of backlog to store.
};


#endif
